Skip to content

Modernize and optimize pixel format operations across platforms.#2645

Merged
JimBobSquarePants merged 14 commits into
mainfrom
js/pixelsformats
Jan 30, 2024
Merged

Modernize and optimize pixel format operations across platforms.#2645
JimBobSquarePants merged 14 commits into
mainfrom
js/pixelsformats

Conversation

@JimBobSquarePants
Copy link
Copy Markdown
Member

@JimBobSquarePants JimBobSquarePants commented Jan 15, 2024

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

Fixes #2232 and #594

  • Updates API to use static interface methods for improved usability and extensibility.
  • Optimizes and modernizes SIMD pixel shuffle operations, adding ARM support.
  • Optimizes and modernizes SIMD Vector4 packing operations. I'm leaving this for a follow up PR

I don't expect anyone to reasonably find the time to analyze this line-by-line as it's a massive piece of work. However, changes on the whole are simplistic and the developer benefit massive.

API change Elevator Pitch

The abomination that is.

TPixel pixel = default;
pixel.FromScaledVector(vector)

Becomes, as nature intended.

TPixel pixel = TPixel.FromScaledVector(vector);

All other conversion methods follow suit where applicable.

@JimBobSquarePants JimBobSquarePants added this to the v4.0.0 milestone Jan 15, 2024
/// <param name="numBytes">The number of bytes to shift by.</param>
/// <returns>The <see cref="Vector128{Byte}"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<byte> ShiftRightBytesInVector(Vector128<byte> value, [ConstantExpected(Max = (byte)15)] byte numBytes)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's something not right in one of the methods below on Arm64.

@tannergooding @saucecontrol Would either of you be able to do a quick readthrough and set me right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Figured it out. It was left shift. I'd forgotten to offset.

Comment thread src/ImageSharp/Common/Helpers/Vector128Utilities.cs Outdated
/// Calculates <paramref name="x"/> % 4
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nuint Modulo4(nuint x) => x & 3;
Copy link
Copy Markdown
Contributor

@tannergooding tannergooding Jan 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noting that x % 4 should already get optimized to x & 3 by the JIT since x is nuint (and therefore definitely unsigned)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks yeah, I just kept it the same for consistency.

Comment on lines +260 to +266
ref Vector512<float> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector512<float> vd0 = ref Unsafe.Add(ref destinationBase, i);

vd0 = Avx.Permute(vs0, control);
Unsafe.Add(ref vd0, 1) = Avx.Permute(Unsafe.Add(ref vs0, 1), control);
Unsafe.Add(ref vd0, 2) = Avx.Permute(Unsafe.Add(ref vs0, 2), control);
Unsafe.Add(ref vd0, 3) = Avx.Permute(Unsafe.Add(ref vs0, 3), control);
vd0 = Vector512Utilities.Shuffle(vs0, control);
Unsafe.Add(ref vd0, (nuint)1) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control);
Unsafe.Add(ref vd0, (nuint)2) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control);
Unsafe.Add(ref vd0, (nuint)3) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control);
Copy link
Copy Markdown
Contributor

@tannergooding tannergooding Jan 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting that you could do this like the following:

Vector512Utilities.Shuffle(Vector512.LoadUnsafe(ref sourceBase, i + 0), control).StoreUnsafe(ref destinationBase, i + 0).
Vector512Utilities.Shuffle(Vector512.LoadUnsafe(ref sourceBase, i + 1), control).StoreUnsafe(ref destinationBase, i + 1).
Vector512Utilities.Shuffle(Vector512.LoadUnsafe(ref sourceBase, i + 2), control).StoreUnsafe(ref destinationBase, i + 2).
Vector512Utilities.Shuffle(Vector512.LoadUnsafe(ref sourceBase, i + 3), control).StoreUnsafe(ref destinationBase, i + 3).

It shouldn't result in any codegen differences, but avoids needing to manipulate the byref and uses direct hardware intrinsic APIs, rather than the Unsafe helpers, so can be easier to read/understand at least IMO.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sourceBase is Vector512<float> so Vector512.LoadUnsafe(ref sourceBase, i + 0) would yield Vector512<Vector512<float>>. I could use float as the base ref type but that complicates the offsetting which confuses me and causes me to make mistakes.

Copy link
Copy Markdown
Contributor

@tannergooding tannergooding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just left a couple comments on newer APIs that might help with readability

/// </summary>
/// <returns>The <see cref="PixelTypeInfo"/>.</returns>
#pragma warning disable CA1000
static abstract PixelTypeInfo GetPixelTypeInfo();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious. Is there a specific reason to kick it from the interface and only have it in the implementations? Probably it was never called from the interface and only from specific implementations?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this is just temporary. I had to hack to get the solution to build because we were misusing ApproximateFloatComparer in the tests.

Comment thread src/ImageSharp.ruleset Outdated
<Include Path="..\shared-infrastructure\sixlabors.ruleset" Action="Default" />
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp.Features" RuleNamespace="Microsoft.CodeAnalysis.CSharp.Features">
<Rule Id="IDE0290" Action="None" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also be suppressible via .editorconfig using csharp_style_prefer_primary_constructors = false:none (or a different severity level if you want to block use of primary constructors altogether, such as silent, suggestion, warning, or error)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Yeah, I’ll do that upstream. Not sold on primary constructors. The syntax doesn’t sit right with me.

@JimBobSquarePants JimBobSquarePants marked this pull request as ready for review January 21, 2024 12:17
@JimBobSquarePants JimBobSquarePants changed the title WIP : Modernize and optimize pixel format operations across platforms. Modernize and optimize pixel format operations across platforms. Jan 21, 2024
@JimBobSquarePants JimBobSquarePants requested review from stefannikolei and tocsoft and removed request for stefannikolei January 21, 2024 12:17
This was referenced May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API arch:arm64 area:pixelformats breaking Signifies a binary breaking change.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Performance optimization opportunities in common pixel formats.

6 participants